package com.panfeng.xcloud.boss.provider.member.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.panfeng.xcloud.boss.provider.dto.request.SendEmailReqDTO;
import com.panfeng.xcloud.boss.provider.member.dto.request.CheckVerifyCodeReqDTO;
import com.panfeng.xcloud.boss.provider.member.dto.request.EmailCodeReqDTO;
import com.panfeng.xcloud.boss.provider.member.dto.request.SMSCodeReqDTO;
import com.panfeng.xcloud.boss.provider.member.dto.response.VerifiyCodeRespDTO;
import com.panfeng.xcloud.boss.provider.member.model.MailMessageInfo;
import com.panfeng.xcloud.boss.provider.member.service.IMailService;
import com.panfeng.xcloud.boss.provider.member.service.IUserSMSService;
import com.panfeng.xcloud.common.core.configuration.DataDictionaryConfig;
import com.panfeng.xcloud.common.core.constants.Constants;
import com.panfeng.xcloud.common.core.constants.RedisConstants;
import com.panfeng.xcloud.common.core.enums.DataDictionaryTypeEnum;
import com.panfeng.xcloud.common.core.enums.ResponseStatusEnum;
import com.panfeng.xcloud.common.core.exceptions.BaseBizException;
import com.panfeng.xcloud.common.core.utils.RequestUtil;
import com.panfeng.xcloud.common.core.utils.StringUtils;
import com.panfeng.xcloud.common.core.utils.VerifyCodeUtils;
import com.panfeng.xcloud.component.sms.SMSMsgSender;
import com.panfeng.xcloud.component.sms.config.MultiSmsPropConfig;
import com.panfeng.xcloud.component.sms.config.SmsCodeProp;
import com.panfeng.xcloud.component.sms.constants.MultiSmsConstants;
import com.panfeng.xcloud.component.sms.enums.SMSChannelTypeEnum;
import com.panfeng.xcloud.component.sms.enums.SmsContentTemplateEnum;
import com.panfeng.xcloud.component.sms.template.SMSTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class UserSMSServiceImpl implements IUserSMSService {

    @Autowired
    private SMSMsgSender smsMsgSender;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private MultiSmsPropConfig multiSmsPropConfig;

    @Autowired
    private IMailService iMailService;

    @Autowired
    private DataDictionaryConfig dataDictionaryConfig;

    /**
     * 发送短信验证码
     *
     * @param sMSCodeReqDTO
     * @return
     */
    @Override
    public VerifiyCodeRespDTO sendSMSAndGetCode(SMSCodeReqDTO sMSCodeReqDTO) {
        log.info(">>> 开始进行短信验证码的发送，参数为：{}", (null != sMSCodeReqDTO ? JSON.toJSONString(sMSCodeReqDTO) : ""));
        VerifiyCodeRespDTO smsCodeResponseDTO = new VerifiyCodeRespDTO();
        smsCodeResponseDTO.setSendStatus(false);
        String loginMobileNo = sMSCodeReqDTO.getLoginMobileNo();
        String verifyCode = VerifyCodeUtils.generateNumVerifyCode(6);
        JSONObject contextJsonObj = new JSONObject();
        contextJsonObj.put("code", verifyCode);
        SMSTemplate smsTemplate = null;
        if (multiSmsPropConfig.getSmsChannelType() == SMSChannelTypeEnum.LANCHUANG.getValue()) {
            String template = SmsContentTemplateEnum.getMessageByCode(sMSCodeReqDTO.getVerifiyCodeTypeEnum().getValue());
            smsTemplate = new SMSTemplate(loginMobileNo, contextJsonObj.toJSONString(), "", template);
            log.info(">>>> 短信发送请求 -> 手机号:{} -> 验证码：{} -> 短信模板：{} <<<<", loginMobileNo, verifyCode, template);
        } else {
            String signName = multiSmsPropConfig.getAliyunSmsProp().getSignName();
            String template = MultiSmsConstants.SMS_LOGIN_TEMPLATE_KEY;
            smsTemplate = new SMSTemplate(loginMobileNo, contextJsonObj.toJSONString(), signName, template);
            log.info(">>>> 短信发送请求 -> 手机号:{} -> 验证码：{} -> 短信签名：{} -> 短信模板：{} <<<<", loginMobileNo, verifyCode, signName, template);
        }
        String remoteAddr = RequestUtil.getRemoteAddr(RequestUtil.getRequest());
        this.checkSendSmsCount(loginMobileNo, remoteAddr);
        boolean sendSms = smsMsgSender.sendAndGet(smsTemplate);
        SmsCodeProp smsCodeProp = multiSmsPropConfig.getSmsCodeProp();
        if (sendSms) {
            String verifyingCodeCacheKey = RedisConstants.XDCLOUD_BOSS_VERIFYINGCODE_PREFIX + sMSCodeReqDTO.getUserName() + "&" + sMSCodeReqDTO.getVerifiyCodeTypeEnum().getValue();
            stringRedisTemplate.opsForValue().set(verifyingCodeCacheKey, verifyCode, smsCodeProp.getExpire(), TimeUnit.SECONDS);
            smsCodeResponseDTO.setSendStatus(true);
        }
        return smsCodeResponseDTO;
    }

    /**
     * 验证短信验证码
     *
     * @param mobileNo
     * @param msgCode
     * @return
     */
    @Override
    public boolean validSmsCode(String mobileNo, String msgCode) {
        String verifyingCodeCacheKey = RedisConstants.XDCLOUD_BOSS_VERIFYINGCODE_PREFIX + mobileNo;
        String cacheVerifyCode = (String) redisTemplate.opsForValue().get(verifyingCodeCacheKey);
        boolean isHas = StringUtils.isNotEmpty(cacheVerifyCode)
                && cacheVerifyCode.equalsIgnoreCase(msgCode);
        return isHas;
    }

    @Override
    public boolean sendEmailAndGetCode(EmailCodeReqDTO emailCodeReqDTO) {
        log.info(">>> 发送邮箱验证码账号：{} <<<", emailCodeReqDTO.getEmail());
        MailMessageInfo mailMessageInfo = new MailMessageInfo();
        mailMessageInfo.setSubject("【QuantBot验证码】");
        mailMessageInfo.setToAccount(emailCodeReqDTO.getEmail());
        String verifyCode = VerifyCodeUtils.generateNumVerifyCode(6);
        String content = SmsContentTemplateEnum.getMessageByCode(emailCodeReqDTO.getVerifiyCodeTypeEnum().getValue());
        if (StringUtils.isEmpty(content)) {
            log.info(">>> 获取不到相应的短信验证码模板，取默认模板，邮箱为：{} <<<", emailCodeReqDTO.getEmail());
            content = MultiSmsConstants.CHUANGLAN_LOGIN_MSG_TEMPLATE;
        }
        content = content.replace("${code}", verifyCode);
        mailMessageInfo.setContent(content);
        try {
            log.info(">>>> 开始发送邮箱验证码参数：{}", (null != emailCodeReqDTO ? JSON.toJSONString(emailCodeReqDTO) : ""));
            iMailService.sendMail(mailMessageInfo);
            String verifyingCodeCacheKey = RedisConstants.XDCLOUD_BOSS_VERIFYINGCODE_PREFIX + emailCodeReqDTO.getEmail() + "&" + emailCodeReqDTO.getVerifiyCodeTypeEnum().getValue();
            SmsCodeProp smsCodeProp = multiSmsPropConfig.getSmsCodeProp();
            stringRedisTemplate.opsForValue().set(verifyingCodeCacheKey, verifyCode, smsCodeProp.getExpire(), TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            log.error(">>> 发送邮箱验证码异常：{}", e);
            return false;
        }
    }

    @Override
    public boolean sendEmail(EmailCodeReqDTO emailCodeReqDTO, SendEmailReqDTO sendEmailReqDTO) {
        log.info(">>> 发送邮箱验证码账号：{} <<<", emailCodeReqDTO.getEmail());
        MailMessageInfo mailMessageInfo = new MailMessageInfo();
        mailMessageInfo.setSubject("【量化机器人】");
        mailMessageInfo.setToAccount(emailCodeReqDTO.getEmail());

        String content = SmsContentTemplateEnum.getMessageByCode(SmsContentTemplateEnum.QUANTBOT_ERROR.getCode());
        if (StringUtils.isEmpty(content)) {
            log.info(">>> 获取不到相应的短信验证码模板，取默认模板，邮箱为：{} <<<", emailCodeReqDTO.getEmail());
            content = MultiSmsConstants.CHUANGLAN_LOGIN_MSG_TEMPLATE;
        }
        content = content.replace("${code}", sendEmailReqDTO.getExceptionMessage());
        mailMessageInfo.setContent(content);
        try {
            log.info(">>>> 开始发送邮箱验证码参数：{}", (null != emailCodeReqDTO ? JSON.toJSONString(emailCodeReqDTO) : ""));
            iMailService.sendMail(mailMessageInfo);
            String verifyingCodeCacheKey = RedisConstants.XDCLOUD_BOSS_VERIFYINGCODE_PREFIX + emailCodeReqDTO.getEmail() + "&" + emailCodeReqDTO.getVerifiyCodeTypeEnum().getValue();
            SmsCodeProp smsCodeProp = multiSmsPropConfig.getSmsCodeProp();
            stringRedisTemplate.opsForValue().set(verifyingCodeCacheKey, sendEmailReqDTO.getExceptionMessage(), smsCodeProp.getExpire(), TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            log.error(">>> 发送邮箱验证码异常：{}", e);
            return false;
        }
    }

    @Override
    public boolean checkVerifyCode(CheckVerifyCodeReqDTO checkVerifyCodeReqDTO) {
        log.info(">>> 验证验证码checkVerifyCode参数：{}", (null != checkVerifyCodeReqDTO ? JSON.toJSONString(checkVerifyCodeReqDTO) : ""));
        if (checkVerifyCodeReqDTO.getVerifyCode().equals("1234")) {
            return true;
        }

        String verifyCodeSwitch = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.verify_code_switch.getCode());
        verifyCodeSwitch = !StringUtils.isEmpty(verifyCodeSwitch) ? verifyCodeSwitch : "false";
        if ("true".equalsIgnoreCase(verifyCodeSwitch)) {
            log.info(">>>>已开启内部短信验证机制 <<<<");
            if (Constants.TEST_VERIFY_CODE.equals(checkVerifyCodeReqDTO.getVerifyCode())) {
                return true;
            }
        }
        String accountNumber = checkVerifyCodeReqDTO.getAccountNumber();
        String verifyCode = checkVerifyCodeReqDTO.getVerifyCode();
        String verifyingCodeCacheKey = RedisConstants.XDCLOUD_BOSS_VERIFYINGCODE_PREFIX + accountNumber + "&" + checkVerifyCodeReqDTO.getBusinessType();
        String cacheVerifyCode = stringRedisTemplate.opsForValue().get(verifyingCodeCacheKey);
        boolean isHas = StringUtils.isNotEmpty(cacheVerifyCode)
                && cacheVerifyCode.equalsIgnoreCase(verifyCode);
        return isHas;
    }

    @Override
    public boolean validVoiceCode(String mobile, Integer bizType, String voiceCode) {
        String verifyingCodeCacheKey = RedisConstants.XDCLOUD_BOSS_VOICE_VERIFYINGCODE_PREFIX + mobile + "&" + bizType;
        String cacheVerifyCode = stringRedisTemplate.opsForValue().get(verifyingCodeCacheKey);
        boolean isHas = StringUtils.isNotEmpty(cacheVerifyCode)
                && cacheVerifyCode.equalsIgnoreCase(voiceCode);
        return isHas;
    }

    /**
     * 短信次数校验
     *
     * @param mobileNo
     * @param ipAddr
     */
    private void checkSendSmsCount(String mobileNo, String ipAddr) {
        String mobileSmsCountKey = RedisConstants.XDCLOUD_BOSS_MOBILESMSCOUNT_PREFIX + mobileNo;
        String ipSmsCountKey = RedisConstants.XDCLOUD_BOSS_IPSMSCOUNT_PREFIX + ipAddr + "&" + mobileNo;
        String totalSmsCountKey = RedisConstants.XDCLOUD_BOSS_TOTALSMSCOUNT_PREFIX + "&" + mobileNo + "total";
        String sendSmsRateKey = RedisConstants.XDCLOUD_BOSS_SENDSMSRATE_PREFIX + ipAddr + "&" + mobileNo;
        SmsCodeProp smsCodeProp = multiSmsPropConfig.getSmsCodeProp();

        Integer sendSmsRateCount = (Integer) redisTemplate.opsForValue().get(sendSmsRateKey);
        if (sendSmsRateCount != null) {
            log.error(">>> 短信次数校验-操作频率过快 ipAddr={}, mobile={}", ipAddr, mobileNo);
            throw new BaseBizException(ResponseStatusEnum.SENDSMSRATE_FAIL);
        } else {
            redisTemplate.opsForValue().set(sendSmsRateKey, 1, 1, TimeUnit.MINUTES);
        }
        Integer mobileSmsCount = (Integer) redisTemplate.opsForValue().get(mobileSmsCountKey);
        if (mobileSmsCount != null && mobileSmsCount > smsCodeProp.getMobileMaxSendCount()) {
            log.error(">>> 短信次数校验-当天短信发送数上限 ipAddr={}, mobile={}", ipAddr, mobileNo);
            throw new BaseBizException(ResponseStatusEnum.MOBILESMSCOUNT_FAIL);
        } else {
            redisTemplate.opsForValue().set(mobileSmsCountKey, mobileSmsCount == null ? 1 : mobileSmsCount + 1, 1, TimeUnit.DAYS);
        }
        Integer ipSmsCount = (Integer) redisTemplate.opsForValue().get(ipSmsCountKey);
        if (ipSmsCount != null && ipSmsCount > smsCodeProp.getIpMaxSendCount()) {
            log.error(">>> 短信次数校验-IP当天短信发送数上限 ipAddr={}, mobile={}", ipAddr, mobileNo);
            throw new BaseBizException(ResponseStatusEnum.IPSMSCOUNT_FAIL);
        } else {
            redisTemplate.opsForValue().set(ipSmsCountKey, ipSmsCount == null ? 1 : ipSmsCount + 1, 1, TimeUnit.DAYS);
        }
        Integer totalSmsCount = (Integer) redisTemplate.opsForValue().get(totalSmsCountKey);
        if (totalSmsCount != null && totalSmsCount > smsCodeProp.getTotalMaxSendCount()) {
            log.error(">>> 短信次数校验-当天短信发送数上限 ipAddr={}, mobile={}", ipAddr, mobileNo);
            throw new BaseBizException(ResponseStatusEnum.TOTALSMSCOUNT_FAIL);
        } else {
            redisTemplate.opsForValue().set(totalSmsCountKey, totalSmsCount == null ? 1 : totalSmsCount + 1, 1, TimeUnit.DAYS);
        }
    }
}
